package com.dss.sdk.api.base;

import com.dss.sdk.annotation.IgnoreField;
import com.dss.sdk.annotation.ValidField;
import com.dss.sdk.exception.ApiRuleException;
import com.dss.sdk.file.FileItem;
import com.dss.sdk.request.DssRequest;
import com.dss.sdk.utils.string.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 基础Fdd请求类，存放一些通用的请求参数。注：这个类不能随意增加get/set/is开头类的方法，否则会有可能和API业务级的参数冲突。
 *
 * @author Fadada
 */
public abstract class BaseDssRequest implements DssRequest {
    @IgnoreField
    public static final String ERROR_CODE_ARGUMENTS_MISSING = "40";
    @IgnoreField
    public static final String ERROR_CODE_ARGUMENTS_INVALID = "41";
    @IgnoreField
    private static final Logger log = LoggerFactory.getLogger(BaseDssRequest.class);
    /**
     * 客户端生成请求id
     */
    @IgnoreField
    private final String reqId = StrUtil.fastSimpleUUID();
    /**
     * 请求方法
     */
    @IgnoreField
    private String httpMethod = "POST";

    @Override
    public String getHttpMethod() {
        return this.httpMethod;
    }

    public void setHttpMethod(String httpMethod) {
        this.httpMethod = httpMethod;
    }

    @Override
    public String getHttpId() {
        return this.reqId;
    }

    @Override
    public Map<String, Object> toMap() throws ApiRuleException {
        Map<String, Object> map = new HashMap(8);
        Field[] fields = getAllFields(getClass());
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (FileItem.class.getTypeName().equals(field.getType().getTypeName())) {
                continue;
            }
            String key = field.getName();
            try {
                setFieldAccessible(field);
                Object object = field.get(this);
                IgnoreField validation = field.getAnnotation(IgnoreField.class);
                if (validation != null) {
                    continue;
                }
                if ((null != object) && (List.class.isAssignableFrom(object.getClass()))) {
                    ParameterizedType listGenericType = (ParameterizedType) field.getGenericType();
                    Type[] listActualTypeArguments = listGenericType.getActualTypeArguments();
                    Type listActualTypeArgument = listActualTypeArguments[0];
                    Class<?> itemType = null;
                    if ((listActualTypeArgument instanceof Class)) {
                        itemType = (Class) listActualTypeArgument;
                    }
                    ArrayList<Object> arrayField = (ArrayList) object;
                    ArrayList<Object> fieldList = new ArrayList();
                    for (int j = 0; j < arrayField.size(); j++) {
                        if ((null != itemType) && (BaseDssRequest.class.isAssignableFrom(itemType))) {
                            Map<String, Object> filedMap = ((BaseDssRequest) arrayField.get(j)).toMap();
                            fieldList.add(filedMap);
                        } else {
                            fieldList.add(arrayField.get(j));
                        }
                    }
                    map.put(key, fieldList);
                } else if ((null != object) && (BaseDssRequest.class.isAssignableFrom(object.getClass()))) {
                    BaseDssRequest baseModel = (BaseDssRequest) object;
                    map.put(key, baseModel.toMap());
                } else {
                    map.put(key, object);
                }
            } catch (Exception e) {
                log.error("client-error:Reflect Field {} Exception:{}", key, StrUtil.blankToDefault(e.getMessage(), e.getLocalizedMessage()));
                throw new ApiRuleException(ERROR_CODE_ARGUMENTS_INVALID, "client-error:Reflect Field arguments:" + key + "");
            }
        }
        return map;
    }

    @Override
    public void validate() throws ApiRuleException {
        Field[] fields = getAllFields(getClass());
        for (Field field : fields) {
            try {
                setFieldAccessible(field);
                Object object = field.get(this);
                ValidField validation = field.getAnnotation(ValidField.class);
                if (validation == null) {
                    continue;
                }
                // required
                boolean required = validation.required();
                if (required && null == object) {
                    throw new ApiRuleException(ERROR_CODE_ARGUMENTS_MISSING, "client-error:Missing required arguments:" + field.getName() + "");
                }
                if (required && object instanceof String) {
                    if (((String) object).trim().length() == 0) {
                        throw new ApiRuleException(ERROR_CODE_ARGUMENTS_MISSING, "client-error:Missing required arguments:" + field.getName() + "");
                    }
                }
                //max length
                if (validation.maxLength() > 0 && object instanceof String) {
                    if (((String) object).length() > validation.maxLength()) {
                        throw new ApiRuleException(ERROR_CODE_ARGUMENTS_INVALID, "client-error:Invalid arguments:the string length of " + field.getName() + " can not be larger than " + validation.maxLength() + ".");
                    }
                }
                if (validation.maxLength() > 0 && object instanceof FileItem) {
                    if (((FileItem) object).getFileLength() > validation.maxLength()) {
                        throw new ApiRuleException(ERROR_CODE_ARGUMENTS_INVALID, "client-error:Invalid arguments:the file size of " + field.getName() + " can not be larger than " + validation.maxLength() + ".");
                    }
                }
                if (validation.maxLength() > 0 && object instanceof List) {
                    if (object != null && ((List) object).size() > validation.maxLength()) {
                        throw new ApiRuleException(ERROR_CODE_ARGUMENTS_INVALID, "client-error:Invalid arguments:the array size of " + field.getName() + " must be less than " + validation.maxLength() + ".");
                    }
                }
                if (validation.maxLength() > 0 && object instanceof Long) {
                    if ((Long) object > validation.maxLength()) {
                        throw new ApiRuleException(ERROR_CODE_ARGUMENTS_INVALID, "client-error:Invalid arguments:the array size of " + field.getName() + " must be less than " + validation.maxLength() + ".");
                    }
                }
            } catch (Exception e) {
                if (e instanceof ApiRuleException) {
                    throw (ApiRuleException) e;
                } else {
                    throw new ApiRuleException(ERROR_CODE_ARGUMENTS_INVALID, "client-error:Get Field " + field.getName() + " is Exception");
                }
            }
        }
    }

    private void setFieldAccessible(Field field) {
        boolean isAccess = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) && !field.isAccessible();
        if (isAccess) {
            field.setAccessible(true);
        }
    }

    /**
     * 获取类的所有字段，包括继承的字段，但不包括重复的字段。
     *
     * @param clazz 类的Class对象
     * @return 字段数组
     */
    public static Field[] getAllFields(Class<?> clazz) {
        // 使用LinkedHashSet来保持插入顺序并排除重复
        Set<Field> allFieldsSet = new LinkedHashSet<>();
        Field[] fields = clazz.getDeclaredFields();
        allFieldsSet.addAll(java.util.Arrays.asList(fields));

        Class<?> currentClass = clazz.getSuperclass();
        // 遍历所有父类
        while (currentClass != null) {
            fields = currentClass.getDeclaredFields();
            allFieldsSet.addAll(java.util.Arrays.asList(fields));
            // 移动到下一个父类
            currentClass = currentClass.getSuperclass();
        }
        // 转换为数组并返回
        return allFieldsSet.toArray(new Field[0]);
    }
}
